package util.ui;

import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JPanel;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.Box;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.Font;
import java.awt.ItemSelectable;
import java.awt.Insets;

import java.util.Vector;
import java.util.regex.Pattern;
import java.util.regex.Matcher;


/**
 * @author matt
 *
 */

// mdb added 10/31/07
public class ListField extends JPanel implements ListSelectionListener, ItemSelectable {
	private static final long serialVersionUID = 4021248191275900142L;
	private JTextField field;
	private JButton btnSearch;
	private JButton btnSelectAll;
	private JButton btnUnselectAll;
	private JList list;
	private JLabel lblCount;
	private JLabel lblSearch;
	private JTextArea txtHelp;
	private Object[] listCopy;
	private JScrollPane scroll;
	private String fieldTextCopy = null;
	private ItemListener itemListener = null;
	
	public ListField(int columns, Object[] listItems, ItemListener l) {		
		field  = new JTextField(columns);
		field.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				btnSearch.doClick();
			}
		});
		
		btnSearch = new JButton("search");
		btnSearch.setMargin(new Insets(0, 0, 0, 0));
		btnSearch.setFont(new Font(btnSearch.getFont().getName(),Font.PLAIN,10));
		btnSearch.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				searchList();
				fireItemEvent();
			}
		});
		
		JPanel topPanel = new JPanel();
		topPanel.setLayout(new BoxLayout ( topPanel, BoxLayout.X_AXIS ));
		lblSearch = new JLabel("Name: ");
		lblSearch.setFont(new Font(lblSearch.getFont().getName(), Font.PLAIN, 10));
		topPanel.add(lblSearch);
		topPanel.add(field);
		topPanel.add(Box.createHorizontalStrut(2));
		topPanel.add(btnSearch);
		
		lblCount = new JLabel("Loading ...", JLabel.CENTER); // set by setListData()
		lblCount.setFont(new Font(lblCount.getFont().getName(),Font.PLAIN,10));
		
		listCopy = new Object[0];
		list = new JList();
		setListData(listItems);
		list.addListSelectionListener(this);
		itemListener = l;
		
		scroll = new JScrollPane(list);
		
		btnSelectAll = new JButton("select all");
		btnSelectAll.setMargin(new Insets(0, 0, 0, 0));
		btnSelectAll.setFont(new Font(btnSelectAll.getFont().getName(),Font.PLAIN,10));
		btnSelectAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				selectAll();
				fireItemEvent();
			}
		});
		
		btnUnselectAll = new JButton("unselect all");
		btnUnselectAll.setMargin(new Insets(0, 0, 0, 0));
		btnUnselectAll.setFont(new Font(btnUnselectAll.getFont().getName(),Font.PLAIN,10));
		btnUnselectAll.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				unselectAll();
				fireItemEvent();
			}
		});
		
		JPanel bottomPanel = new JPanel();
		bottomPanel.setLayout(new BoxLayout ( bottomPanel, BoxLayout.X_AXIS ));
		bottomPanel.add(btnSelectAll);
		bottomPanel.add(Box.createHorizontalStrut(5));
		bottomPanel.add(btnUnselectAll);
		bottomPanel.add(Box.createHorizontalStrut(10));
		bottomPanel.add(Box.createHorizontalGlue());
		bottomPanel.add(lblCount);
		
		txtHelp = new JTextArea("Click \"Include Selection\" and select the desired\n" +
								"items in the list.  Enter a name and click \"Search\"\n" +
								"to filter the items shown in the list.\n");
		
		setLayout(new BoxLayout ( this, BoxLayout.Y_AXIS ));
		add(topPanel);
		add(scroll);
		add(bottomPanel);
		add(txtHelp);
	}
	
	public void setListData(Object[] listData) {
		if (listData == null)
			return;
		
		DefaultListModel model = new DefaultListModel();
		listCopy = new Object[listData.length];
		System.arraycopy(listData, 0, listCopy, 0, listData.length);
		for (int i = 0;  i < listData.length;  i++)
			model.addElement(listData[i]);
		list.setModel(model);
		lblCount.setText("Showing "+listCopy.length+" of "+listCopy.length);
	}
	
	private void addMatches(DefaultListModel model, String strRegEx) {
		Pattern p = Pattern.compile(strRegEx, Pattern.CASE_INSENSITIVE);
		Matcher m;
		Boolean isZeroLength = (strRegEx.length() == 0); // for speed, any improvement?
		String s2;
		
		for (int i = 0;  i < listCopy.length;  i++) {
			s2 = listCopy[i].toString();
			m = p.matcher(s2);
			if (isZeroLength || m.matches())
				model.addElement(s2);
		}
	}
	
	public void searchList() {
		//DefaultListModel model = (DefaultListModel)list.getModel();
		DefaultListModel model = new DefaultListModel(); // new model fixes performance problem after select all
		String s = field.getText().trim();
		
		if (s.length() == 0 && list.getModel().getSize() == listCopy.length) return;
		
		s = s.replaceAll("\\*", ".*");
		
		if (s != fieldTextCopy) {
			//model.removeAllElements();
			addMatches(model, s);
			if (model.getSize() == 0 && !s.startsWith(".*") && !s.endsWith(".*"))
				addMatches(model, ".*"+s+".*"); // search again with wildcards at each end
			
			list.setModel(model);
			fieldTextCopy = new String(s);
		}
		
		lblCount.setText("Showing "+list.getModel().getSize()+" of "+listCopy.length);
	}
	
	private void fireItemEvent() {
		if (itemListener != null)
			itemListener.itemStateChanged(
				new ItemEvent(this, 0, (Object)list, ItemEvent.ITEM_STATE_CHANGED));	
	}
	
	public void valueChanged(ListSelectionEvent e) {
		fireItemEvent();
	}
	
	public void addItemListener(ItemListener l) { } 		// don't use
	public Object[] getSelectedObjects() { return null; } 	// don't use
	public void removeItemListener(ItemListener l) { } 		// don't use
	
	public int getSelectionCount() {
		return list.getSelectedIndices().length;
	}
	
	public Vector<String> getSelection() {
		Object[] objs = list.getSelectedValues();
		Vector<String> v = new Vector<String>();
		
		if (objs.length == 0) { // return all elements in list
			for (int i = 0;  i < list.getModel().getSize();  i++)
				v.add(list.getModel().getElementAt(i).toString());
		}
		else { // return only selected elements
			for (int i = 0;  i < objs.length;  i++)
				v.add(objs[i].toString());
		}
		
		return v;
	}
	
	public void setSelection(Vector<String> sel) {
		DefaultListModel model = (DefaultListModel)list.getModel();	
		int[] indices = new int[sel.size()];
		
		for (int i = 0, j = 0;  i < model.getSize();  i++) {
			if (sel.contains(model.getElementAt(i)))
				indices[j++] = i;
		}
		
		list.setSelectedIndices(indices);
		if (indices.length > 0) list.ensureIndexIsVisible(indices[0]);
	}
	
	public void selectAll() {
		int size = list.getModel().getSize();
		if (size > 0) list.setSelectionInterval(0, size-1);
	}
	
	public void unselectAll() {
		list.clearSelection();
	}
	
	public boolean areAllSelected() {
		return ((getSelectionCount() == 0 
					&& list.getModel().getSize() == listCopy.length)
					|| getSelectionCount() == listCopy.length);
	}
	
	public void setEnabled(boolean enabled) {
		field.setEnabled(enabled);
		btnSearch.setEnabled(enabled);
		list.setEnabled(enabled);
		btnSelectAll.setEnabled(enabled);
		btnUnselectAll.setEnabled(enabled);
		lblCount.setEnabled(enabled);
		lblSearch.setEnabled(enabled);
		//txtHelp.setEnabled(enabled);
	}
}